const fs = require('fs');
const path = require('path');
const prompts = require('prompts');
const unionBy = require('lodash/unionBy');

// 模板路径
const TEMPLATE_PATH = '../template';
// 允许赋值的文件类型
const ASSIGNMENT_FILE_EXTNAME = /\.(js(x)?|ts(x)?|html|less|css|md)$/;
// 变量解析规则
const VARIABLE_RULE = /\/\/(\s)?(\$\$\w+)(\s)?:(\s)?(.+)(\s)?=(\s)?(.+)/;

// 变量语法匹配
const VAR_RULE = /^\$\$\w+$/;
// if 语法匹配
const IF_RULE = /^\/\/\s*?if\s*?(\!|not)*?\s*?(\$\$\w+)\s*?(\{)*?\s*?$/;
// else 语法匹配
const ELSE_RULE = /^\/\/\s*?(else|(\})\s*?else\s*?(\{))\s*?$/;
// end 语法匹配
const END_RULE = /^\/\/\s*?(\})\s*?$/;
// for 语法匹配
const FOR_RULE = /^\/\/\s*?for\s*?(\$\$\w+)\s+in\s+(\d+|\$\$\w+)\s*?\{\s*?$/;

// 当前环境用户输入的变量值缓存
const VARIABLE_CACHE = {};

// 用户输入提示前缀
const PROMPT_TIP_PREFIX = {
  confirm: '请确认 ',
  text: '请输入 ',
  number: '请输入 ',
};

function transStr(str) {
  if (/\d+/.test(str)) return Number(str);
  if (/true|false/.test(str)) return str === 'true';
  return str;
}

function getPromptType(str) {
  if (/\d+/.test(str)) return 'number';
  if (/true|false/.test(str)) return 'confirm';
  return 'text';
}

// 查找模板目录下的子项
function findTemplate(target) {
  try {
    const templatePath = path.join(__dirname, TEMPLATE_PATH);
    const files = fs.readdirSync(templatePath);
    for (const file of files) {
      if (path.basename(file, path.extname(file)) === target) {
        return path.join(templatePath, file);
      }
    }
  } catch (err) {
    console.error('读取目录时发生错误:', err);
  }
}

// 处理内容里的逻辑语句
function contentSyntaxParse(lines) {
  let contentResult = [];
  // -1 丢弃行数据，1或0 拼接行数据，2 记录循环行数据
  let mode = 0;
  let multiple = false;
  let for_index_Variable = null;
  let for_times = 0;
  let for_lines = [];

  for (let index = 0; index < lines.length; index++) {
    const readline = lines[index];
    // TODO: 使用 stack 处理语法嵌套的问题
    if (END_RULE.test(readline)) {
      if (mode === 2) {
        for (let i = 0; i < for_times; i++) {
          const lines = for_lines.map((text) => text.replaceAll(for_index_Variable, i));
          contentResult = contentResult.concat(lines);
        }
        for_lines = [];
      }
      mode = 0;
      continue;
    }

    if (ELSE_RULE.test(readline)) {
      mode *= -1;
      continue;
    }

    if (IF_RULE.test(readline)) {
      if (mode !== 0) throw Error(`❌ if 语句不支持嵌套 -> ${readline}`);
      const ifRule = IF_RULE.exec(readline);
      const not = !!ifRule[1];
      const variableKey = ifRule[2];
      multiple = !!ifRule[3];
      mode = (not && !VARIABLE_CACHE[variableKey]) || !!VARIABLE_CACHE[variableKey] ? 1 : -1;
      continue;
    }

    if (FOR_RULE.test(readline)) {
      if (mode !== 0) throw Error(`❌ for 语句不支持嵌套 -> ${readline}`);
      const forRule = FOR_RULE.exec(readline);
      for_index_Variable = forRule[1];
      if (VAR_RULE.test(forRule[2])) {
        for_times = VARIABLE_CACHE[forRule[2]];
        if (!/\d+/.test(for_times)) throw Error(`❌ 未定义的变量 -> ${forRule[2]}`);
      } else {
        for_times = Number(forRule[2]);
      }
      for_times = Math.max(for_times, 1);
      for_times = Math.min(for_times, 65535);
      mode = 2;
      continue;
    }

    if (mode === 2) {
      for_lines.push(readline);
      continue;
    }

    if (mode === -1) {
      if (multiple) continue;
      mode = 0;
      continue;
    }

    contentResult.push(readline);
    if (multiple) continue;
    mode = 0;
  }

  return contentResult;
}

// 将内容定义的变量进行赋值，然后写入到目标路径
async function copyFileAndAssignment(sourcePath, targetPath) {
  // 读取文件内容赋值为 fileContent
  const fileContent = fs.readFileSync(sourcePath, 'utf8');
  // 解析文件内容的头部以 // $$\w 开头的变量定义，变量定义格式为以 // $$字母: 变量描述=变量默认值
  const lines = fileContent.split(/\n/);
  const undefineVariables = [];

  let startLine = 0;
  for (const index in lines) {
    startLine = index;
    if (!VARIABLE_RULE.test(lines[index])) break;
    const define = VARIABLE_RULE.exec(lines[index]);
    const variableItem = { name: define[2], label: define[5], defaultValue: transStr(define[8]) };
    if (VARIABLE_CACHE[variableItem.name] !== void 0) continue;
    undefineVariables.push(variableItem);
  }

  if (undefineVariables.length > 0) {
    const inputVariables = unionBy(undefineVariables, 'name').map((item) => {
      const type = getPromptType(item.defaultValue);
      return {
        type,
        name: item.name,
        message: `${PROMPT_TIP_PREFIX[type]}${item.label}：`,
        initial: item.defaultValue,
      };
    });

    const userData = await prompts(inputVariables);

    Object.assign(VARIABLE_CACHE, userData);

    if (Object.keys(userData).length < inputVariables.length) {
      throw Error('用户输入终止！');
    }
  }

  const contentLines = contentSyntaxParse(lines.slice(startLine));

  let content = contentLines.join('\n');

  for (const name in VARIABLE_CACHE) {
    const value = VARIABLE_CACHE[name];
    if (value !== void 0) content = content.replaceAll(name, value);
  }

  // 将内容写入文件
  fs.writeFileSync(targetPath, content, 'utf8');
}

// 递归拷贝函数
async function copyDirectory(src, dest) {
  // 读取源目录内容
  const files = fs.readdirSync(src);

  // 遍历文件和文件夹
  for (const file of files) {
    const srcPath = path.join(src, file);
    const destPath = path.join(dest, file);

    // 检查是否为文件夹
    if (fs.lstatSync(srcPath).isDirectory()) {
      // 如果是文件夹，递归拷贝
      if (!fs.existsSync(destPath)) {
        fs.mkdirSync(destPath, { recursive: true });
      }
      await copyDirectory(srcPath, destPath);
    } else {
      if (ASSIGNMENT_FILE_EXTNAME.test(srcPath)) {
        await copyFileAndAssignment(srcPath, destPath);
      } else {
        // 如果是文件，拷贝文件
        fs.copyFileSync(srcPath, destPath);
      }
    }
  }
}

async function main() {
  const runPath = process.env.INIT_CWD;

  const args = process.argv.slice(2);

  const [templateName, defineName] = args;

  const templateFilePath = findTemplate(templateName);

  if (!templateFilePath) {
    return console.log('⛔️ 不存在模板：', templateName);
  }

  let targetDirPath = null;
  // 判断 templateFilePath 是文件还是文件夹
  if (fs.lstatSync(templateFilePath).isFile()) {
    // 如果是文件，拷贝文件到 runPath 下，如果 defineName 有值，将重命名为 defineName
    const rename = !defineName ? path.basename(templateFilePath) : defineName + path.extname(templateFilePath);
    targetDirPath = path.join(runPath, rename);

    if (ASSIGNMENT_FILE_EXTNAME.test(templateFilePath)) {
      await copyFileAndAssignment(templateFilePath, targetDirPath);
    } else {
      fs.copyFileSync(templateFilePath, targetDirPath);
    }
  } else if (fs.lstatSync(templateFilePath).isDirectory()) {
    // 如果是 templateFilePath 是文件夹
    targetDirPath = path.join(runPath, defineName || templateName);

    // 判断 runPath 下是否存在此文件夹
    if (!fs.existsSync(targetDirPath)) {
      // 如果不存在，创建此文件夹
      fs.mkdirSync(targetDirPath, { recursive: true });
    }

    // 递归拷贝 templateFilePath 下文件夹，到之前的目录
    await copyDirectory(templateFilePath, targetDirPath);
  }
  console.log('🎉 添加成功 -> ', targetDirPath);
}

main();
